#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
包装类型识别脚本
仅通过判断Excel文件中的单元格是否为合并单元格来判定包装类型
支持分步测试功能
"""

import os
import sys
import pandas as pd
import logging
from datetime import datetime
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties

# 添加项目根目录到Python路径
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

# 导入自定义模块
try:
    from src.excel_handler.precise_merged_cell_detector import PreciseMergedCellDetector
    from src.utils.logger import setup_logger
    logger = setup_logger('fix_packaging_types')
    logger.info("已成功导入自定义模块")
except Exception as e:
    print(f"导入模块失败: {str(e)}")
    # 使用内置logging作为备选
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    logger = logging.getLogger('fix_packaging_types')
    logger.error(f"导入自定义模块失败，使用内置logging: {str(e)}")

# 配置路径
TEST_RESULTS_DIR = os.path.join(os.path.dirname(__file__), 'test_results', 'processed_data')
INPUT_FILE = os.path.join(TEST_RESULTS_DIR, '2-1-历史数据包装类型判定.csv')
BAK_FILE = os.path.join(TEST_RESULTS_DIR, '2-1-历史数据包装类型判定.csv.bak')
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Excel源文件
EXCEL_FILES = [
    os.path.join(ROOT_DIR, '瀚越已发货包装数据总表.xlsx'),
    os.path.join(ROOT_DIR, '瀚的发货用包装箱任务.xlsx'),
    os.path.join(ROOT_DIR, '瀚下发货用包装箱任务查询结果_20250926.xlsx')
]

class PackagingTypeIdentifier:
    """
    包装类型识别器类，仅通过合并单元格判断包装类型
    """
    
    def __init__(self):
        """
        初始化识别器
        """
        self.detector = None
        try:
            # 初始化合并单元格检测器
            self.detector = PreciseMergedCellDetector()
            logger.info("初始化PreciseMergedCellDetector成功")
        except Exception as e:
            logger.error(f"初始化合并单元格检测器失败: {str(e)}")
            raise
    
    def ensure_directory(self, directory):
        """
        确保目录存在
        """
        try:
            os.makedirs(directory, exist_ok=True)
            logger.info(f"确保目录存在: {directory}")
            return True
        except Exception as e:
            logger.error(f"创建目录失败: {str(e)}")
            print(f"创建目录失败: {str(e)}")
            return False
    
    def generate_test_data(self):
        """
        从Excel文件生成测试数据
        """
        logger.info("开始生成测试数据...")
        
        # 确保目录存在
        if not self.ensure_directory(TEST_RESULTS_DIR):
            return None
        
        # 查找可用的Excel文件
        excel_file = None
        for file in EXCEL_FILES:
            if os.path.exists(file):
                excel_file = file
                break
        
        if not excel_file:
            logger.error("未找到可用的Excel源文件")
            print("未找到可用的Excel源文件")
            return None
        
        logger.info(f"使用Excel文件生成测试数据: {excel_file}")
        
        try:
            # 使用pandas读取Excel
            df = pd.read_excel(excel_file)
            logger.info(f"成功读取Excel文件，共{len(df)}行数据")
            
            # 添加包装类型列（如果不存在）
            if '包装类型' not in df.columns:
                df['包装类型'] = ''
            
            # 保存为测试数据文件
            df.to_csv(INPUT_FILE, index=False, encoding='utf-8-sig')
            logger.info(f"已生成测试数据文件: {INPUT_FILE}")
            print(f"已生成测试数据文件: {INPUT_FILE}")
            
            return df
        except Exception as e:
            logger.error(f"生成测试数据失败: {str(e)}")
            print(f"生成测试数据失败: {str(e)}")
            return None
    
    def load_data(self, file_path):
        """
        加载数据文件，如果不存在则尝试生成
        """
        try:
            if not os.path.exists(file_path):
                logger.info(f"数据文件不存在，尝试生成: {file_path}")
                return self.generate_test_data()
            
            logger.info(f"开始加载数据文件: {file_path}")
            df = pd.read_csv(file_path)
            logger.info(f"成功加载数据，共{len(df)}行")
            return df
        except Exception as e:
            logger.error(f"加载数据文件失败: {str(e)}")
            print(f"加载数据文件失败: {str(e)}")
            return None
    
    def identify_packaging_types(self, df, excel_file_path):
        """
        仅基于合并单元格识别包装类型
        
        Args:
            df: 数据框
            excel_file_path: Excel文件路径
            
        Returns:
            处理后的数据框
        """
        logger.info(f"开始基于合并单元格识别包装类型")
        
        # 直接使用detect_combinatorial_packaging方法，仅基于合并单元格信息
        result_df = self.detector.detect_combinatorial_packaging(df, excel_file_path)
        
        # 统计结果
        combined_count = len(result_df[result_df['包装类型'] == '组合包装'])
        individual_count = len(result_df) - combined_count
        logger.info(f"包装类型识别完成 - 组合包装: {combined_count}, 独立包装: {individual_count}")
        
        return result_df
    
    def save_data(self, df, file_path, backup=True):
        """
        保存数据文件
        """
        try:
            print(f"\n=== 保存识别结果 ===")
            # 如果需要备份且文件存在
            if backup and os.path.exists(file_path):
                # 如果备份文件已存在，添加时间戳
                if os.path.exists(BAK_FILE):
                    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
                    backup_with_timestamp = f"{BAK_FILE}.{timestamp}"
                    os.rename(BAK_FILE, backup_with_timestamp)
                    logger.info(f"已重命名旧备份文件为: {backup_with_timestamp}")
                # 创建新的备份
                os.rename(file_path, BAK_FILE)
                logger.info(f"已创建备份文件: {BAK_FILE}")
                print(f"✓ 已创建备份文件: {os.path.basename(BAK_FILE)}")
            
            # 保存识别后的数据
            df.to_csv(file_path, index=False, encoding='utf-8-sig')
            logger.info(f"成功保存识别后的数据到: {file_path}")
            print(f"✓ 成功保存识别后的数据到: {os.path.basename(file_path)}")
            return True
        except Exception as e:
            logger.error(f"保存数据文件失败: {str(e)}")
            print(f"✗ 保存数据文件失败: {str(e)}")
            # 如果有备份，尝试恢复
            if backup and os.path.exists(BAK_FILE):
                try:
                    os.rename(BAK_FILE, file_path)
                    logger.info(f"已从备份恢复原文件")
                    print(f"✓ 已从备份恢复原文件")
                except:
                    logger.error("恢复原文件失败")
                    print("✗ 恢复原文件失败")
            return False
    
    def run_step_by_step(self, input_file, output_file=None):
        """
        分步运行识别流程
        """
        print("=== 开始分步测试包装类型识别流程 ===")
        logger.info("开始分步运行包装类型识别流程")
        
        try:
            # 如果没有指定输出文件，使用输入文件
            if output_file is None:
                output_file = input_file
            
            # 确保目录存在
            if not self.ensure_directory(TEST_RESULTS_DIR):
                return False
            
            # 1. 加载数据（如果不存在会自动生成）
            print(f"\n=== 加载数据文件 ===")
            df = self.load_data(input_file)
            if df is None:
                logger.error("无法继续识别流程，数据加载失败")
                print("✗ 数据加载失败，无法继续")
                return False
            print(f"✓ 成功加载数据，共{len(df)}行")
            
            # 2. 查找可用的Excel文件用于识别合并单元格
            excel_file = None
            for file in EXCEL_FILES:
                if os.path.exists(file):
                    excel_file = file
                    break
            
            if not excel_file:
                logger.error("未找到可用的Excel源文件，无法识别合并单元格")
                print("✗ 未找到可用的Excel源文件，无法识别合并单元格")
                return False
            
            # 3. 基于合并单元格识别包装类型
            df = self.identify_packaging_types(df, excel_file)
            
            # 4. 保存结果
            success = self.save_data(df, output_file)
            
            # 5. 生成可视化
            if success:
                self.generate_visualization(df)
            
            if success:
                logger.info("包装类型分步识别流程完成")
                print("\n🎉 包装类型分步识别流程完成！")
            
            return success
        except Exception as e:
            logger.error(f"运行分步识别流程失败: {str(e)}")
            print(f"\n✗ 运行分步识别流程失败: {str(e)}")
            return False
    
    def generate_visualization(self, df):
        """
        生成包装类型分布的可视化图表
        
        Args:
            df: 包含包装类型数据的数据框
            
        Returns:
            bool: 是否成功生成可视化
        """
        try:
            print(f"\n=== 生成可视化数据处理文件 ===")
            
            # 确保可视化目录存在
            viz_dir = os.path.join(os.path.dirname(TEST_RESULTS_DIR), 'visualizations')
            self.ensure_directory(viz_dir)
            
            # 统计包装类型
            if '包装类型' not in df.columns:
                logger.error("数据中不包含'包装类型'列，无法生成可视化")
                print("✗ 数据中不包含'包装类型'列，无法生成可视化")
                return False
            
            packaging_counts = df['包装类型'].value_counts()
            combined_count = packaging_counts.get('组合包装', 0)
            individual_count = packaging_counts.get('独立包装', 0)
            
            # 设置中文字体支持
            plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
            plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
            
            # 1. 生成饼图
            plt.figure(figsize=(10, 6))
            labels = ['组合包装', '独立包装']
            sizes = [combined_count, individual_count]
            colors = ['#ff9999', '#66b3ff']
            explode = (0.1, 0)  # 突出组合包装
            
            plt.pie(sizes, explode=explode, labels=labels, colors=colors,
                   autopct='%1.1f%%', shadow=True, startangle=90)
            plt.axis('equal')  # 保证饼图是圆的
            plt.title('包装类型分布饼图')
            
            pie_chart_path = os.path.join(viz_dir, '包装类型分布饼图.png')
            plt.savefig(pie_chart_path, dpi=300, bbox_inches='tight')
            plt.close()
            
            # 2. 生成柱状图
            plt.figure(figsize=(10, 6))
            x = np.arange(len(labels))
            width = 0.6
            
            bars = plt.bar(x, sizes, width, color=colors)
            
            # 添加数据标签
            for bar in bars:
                height = bar.get_height()
                plt.text(bar.get_x() + bar.get_width()/2., height + 10,
                        f'{int(height)}', ha='center', va='bottom')
            
            plt.xlabel('包装类型')
            plt.ylabel('数量')
            plt.title('包装类型分布柱状图')
            plt.xticks(x, labels)
            plt.ylim(0, max(sizes) * 1.1)  # 设置y轴范围，留出空间显示标签
            
            bar_chart_path = os.path.join(viz_dir, '包装类型分布柱状图.png')
            plt.savefig(bar_chart_path, dpi=300, bbox_inches='tight')
            plt.close()
            
            # 3. 生成统计表格（CSV格式）
            stats_df = pd.DataFrame({
                '包装类型': labels,
                '数量': sizes,
                '占比': [f'{size/sum(sizes)*100:.1f}%' for size in sizes]
            })
            
            stats_path = os.path.join(viz_dir, '包装类型统计数据.csv')
            stats_df.to_csv(stats_path, index=False, encoding='utf-8-sig')
            
            logger.info(f"成功生成可视化文件: {pie_chart_path}")
            logger.info(f"成功生成可视化文件: {bar_chart_path}")
            logger.info(f"成功生成统计数据: {stats_path}")
            
            print(f"✓ 成功生成包装类型分布饼图: {os.path.basename(pie_chart_path)}")
            print(f"✓ 成功生成包装类型分布柱状图: {os.path.basename(bar_chart_path)}")
            print(f"✓ 成功生成统计数据表格: {os.path.basename(stats_path)}")
            
            # 显示统计信息
            print("\n📊 包装类型统计详情:")
            print(stats_df.to_string(index=False))
            
            return True
        except Exception as e:
            logger.error(f"生成可视化失败: {str(e)}")
            print(f"✗ 生成可视化失败: {str(e)}")
            return False

def main():
    """
    主函数
    """
    try:
        print("📦 包装类型识别工具 v1.0")
        print("============================")
        print("仅通过Excel合并单元格判定包装类型")
        print("============================")
        
        # 创建识别器实例
        identifier = PackagingTypeIdentifier()
        
        # 运行分步识别流程
        success = identifier.run_step_by_step(INPUT_FILE)
        
        if success:
            print("\n✅ 识别成功！")
            return 0
        else:
            print("\n❌ 识别失败！")
            return 1
    except Exception as e:
        print(f"\n❌ 程序执行异常: {str(e)}")
        import traceback
        traceback.print_exc()
        return 1

if __name__ == "__main__":
    sys.exit(main())